﻿using BlazorAdmin.Logs;
using BootstrapBlazor.Components;
using BlazorAdmin.Tcp.Protocol;
using Microsoft.Extensions.Logging;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Runtime.InteropServices;

namespace BlazorAdmin.Tcp.Server
{
    /// <summary>
    /// Tcp服务类
    /// </summary>
    public class TcpServer<T> where T : TcpToken, new()
    {
        private readonly TcpConnPool<T> connPool;
        private readonly TcpOptions tcpOptions;
        private readonly Thread tcpThread;
        private readonly Socket serverSocket;
        private readonly Semaphore maxNumberAcceptedClients;
        private readonly BufferManager bufferManager;
        private readonly SocketPool readPool;
        private readonly List<string> frameFilter = new();
        private bool isStart;
        private int numConnectedSockets;
        public Func<Color, string, bool>? Console;

        public TcpServer(TcpOptions tcpOptions)
        {
            this.tcpOptions = tcpOptions;
            bufferManager = new(tcpOptions.ReceiveBufferSize, tcpOptions.ReceiveBufferSize * tcpOptions.MaxConnections);
            readPool = new(tcpOptions.MaxConnections);
            connPool = new TcpConnPool<T>(tcpOptions.MaxConnections);
            maxNumberAcceptedClients = new Semaphore(tcpOptions.MaxConnections, tcpOptions.MaxConnections);
            tcpThread = new Thread(new ParameterizedThreadStart(TcpMainMethod));
            serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }

        /// <summary>
        /// 接收到数据帧，如果该连接在过滤器中则引发此事件
        /// </summary>
        public event Action<T, byte[], int, int>? OnRevicedOfInFilter;

        /// <summary>
        /// 接收到数据帧并成功解析，如果该连接在过滤器中则引发此事件
        /// </summary>
        public event Action<T, ProtocolBase>? OnReviced;

        /// <summary>
        /// 获取连接会话
        /// </summary>
        public IEnumerable<T> Connections => connPool.GetConnections();

        /// <summary>
        /// 获取指定客户端ID的连接信息列表
        /// </summary>
        /// <param name="predicate">匹配方法</param>
        /// <returns></returns>
        public IEnumerable<T>? GetConnections(Func<T, bool>? predicate = null) => connPool.GetConnections(predicate);

        /// <summary>
        /// 获取连接会话管理组件
        /// </summary>
        public TcpConnPool<T> TokenBroker => connPool;

        /// <summary>
        /// 将指定会话ID添加到数据帧过滤器
        /// </summary>
        /// <param name="key">会话ID</param>
        /// <returns></returns>
        public bool AddFilter(string key)
        {
            if (frameFilter.Contains(key))
                return true;

            if (connPool.IsExistConectionn(key))
            {
                frameFilter.Add(key);
                return true;
            }

            return false;
        }

        /// <summary>
        /// 将指定会话ID从数据帧过滤器中移除
        /// </summary>
        /// <param name="key">会话ID</param>
        /// <returns></returns>
        public bool RemoveFilter(string key) => frameFilter.Remove(key);

        /// <summary>
        /// 发送指令
        /// </summary>
        /// <param name="data"></param>
        /// <param name="conns"></param>
        public void Send(string data, params T[] conns)
        {
            var buffer = new List<byte> { 0x4C, 0x45, 0x4E, 0x53 }; // 必须包含前缀 LENS

            if (string.IsNullOrEmpty(data) || conns == null || conns.Length < 1)
                return;

            buffer.AddRange(Encoding.UTF8.GetBytes(data));

            T t = conns[0];

            t.Client?.Send(buffer.ToArray());

            LogUtil.Info($"发送消息至 {t.Client?.RemoteEndPoint?.ToString()} ：LENS{data}");
        }

        /// <summary>
        /// 开始服务
        /// </summary>
        /// <returns></returns>
        internal TcpServer<T> Start()
        {
            if (isStart)
                return this;

            isStart = true;
            tcpThread.Start();

            LogUtil.Info($"已启动Socket服务端，开始监听端口：{tcpOptions.Port}，消息缓存：{tcpOptions.ReceiveBufferSize / 1024}KB，最大连接数：{tcpOptions.MaxConnections}");

            return this;
        }

        /// <summary>
        /// 停止服务
        /// </summary>
        internal void Stop()
        {
            isStart = false;

            maxNumberAcceptedClients.Dispose();
        }

        #region 内部方法

        /// <summary>
        /// TCP服务方法入口
        /// </summary>
        /// <param name="state"></param>
        private void TcpMainMethod(object? state)
        {
            Init();
            IPEndPoint endPoint = new(IPAddress.Any, tcpOptions.Port);
            serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);

            int keepAliveTimeSeconds = 60; // 超时时间：Windows默认60秒
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            else
                serverSocket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.TcpKeepAliveTime, keepAliveTimeSeconds * 1000);

            serverSocket.Bind(endPoint);
            serverSocket.Listen(100);

            StartAccept(null);

            while (isStart)
            {
                Thread.Sleep(1000);
            }

            serverSocket.Dispose();

            LogUtil.Info("已关闭Socket服务端");
        }

        /// <summary>
        /// TCP服务初始化
        /// </summary>
        private void Init()
        {
            for (int i = 0; i < tcpOptions.MaxConnections; i++)
            {
                T token = new() { Server = serverSocket };
                SocketAsyncEventArgs readEventArg = new() { UserToken = token };
                readEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                bufferManager.SetBuffer(readEventArg);
                readPool.Push(readEventArg);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        private void IO_Completed(object? sender, SocketAsyncEventArgs e)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    //ProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }
        }

        /// <summary>
        /// 开启侦听
        /// </summary>
        /// <param name="acceptEventArg">异步Socket实体对象</param>
        private void StartAccept(SocketAsyncEventArgs? acceptEventArg)
        {
            if (null == acceptEventArg)
            {
                acceptEventArg = new SocketAsyncEventArgs();
                acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
            }
            else
                acceptEventArg.AcceptSocket = null;

            maxNumberAcceptedClients.WaitOne();
            if (null == serverSocket)
                return;

            bool willRaiseEvent = serverSocket.AcceptAsync(acceptEventArg);
            if (!willRaiseEvent)
                ProcessAccept(acceptEventArg);
        }

        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            Interlocked.Increment(ref numConnectedSockets);
            SocketAsyncEventArgs readEventArgs = readPool.Pop();
            bufferManager.SetBuffer(readEventArgs);

            if (readEventArgs.UserToken is T token)
            {
                token.Client = e.AcceptSocket;
                token.ConnectTime = DateTime.Now;
                token.LastActivityTime = token.ConnectTime;
                connPool.Add(token);

                LogUtil.Info($"加入新的会话连接 {token.Key} {token.ClientPoint}");

                if (null != token.Client)
                {
                    bool willRaiseEvent = token.Client.ReceiveAsync(readEventArgs);
                    if (!willRaiseEvent)
                        ProcessReceive(readEventArgs);
                }
            }

            StartAccept(e);
        }

        private void AcceptEventArg_Completed(object? sender, SocketAsyncEventArgs e) => ProcessAccept(e);

        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            if (e.UserToken is not T token)
                return;

            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                token.LastActivityTime = DateTime.Now;
                Call(token, e.Buffer, e.Offset, e.BytesTransferred);
                if (null != token.Client)
                {
                    bool willRaiseEvent = token.Client.ReceiveAsync(e);
                    if (!willRaiseEvent)
                        ProcessReceive(e);
                }
                //token.CopyReceiveToHistroy(e.Buffer, e.Offset, e.BytesTransferred);
            }
            else
            {
                CloseClientSocket(e, token);

                LogUtil.Error($"会话 {token.Client?.RemoteEndPoint?.ToString()} 收到空包，准备关闭会话连接");
            }
        }

        private void Call(T token, byte[]? buffer, int start, int count)
        {
            var str = Encoding.Default.GetString(buffer);

            if (null == buffer)
                return;

            ProtocolBase? protocol = tcpOptions.GetHandler(buffer, start, count);
            if (null != protocol && null != OnReviced)
                OnReviced(token, protocol);

            if (frameFilter.Contains(token.Key))
                OnRevicedOfInFilter?.Invoke(token, buffer, start, count);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="e"></param>
        /// <param name="token"></param>
        private void CloseClientSocket(SocketAsyncEventArgs e, T token)
        {
            if (token.IsClosing)
                return;

            token.IsClosing = true;
            try
            {
                if (null != token.Client)
                {
                    token.Client.Shutdown(SocketShutdown.Both);
                    token.Client.Close();

                    LogUtil.Info($"已关闭会话连接 {token.Key} {token.ClientPoint}");
                }

                if (connPool.Remove(token.Key))
                {
                    token.Reset();
                    readPool.Push(e);
                    Interlocked.Decrement(ref numConnectedSockets);
                }
            }
            catch (Exception ex)
            {
                LogUtil.Error($"关闭会话连接 {token.Key} {token.ClientPoint} 异常：\n{ex.ToString()}");
            }

            token.IsClosing = false;
        }

        #endregion
    }
}